package com.literaryfamily.literaryfamily.service.user.security;

import com.literaryfamily.literaryfamily.entity.User;
import com.literaryfamily.literaryfamily.entity.UserLogin;
import com.literaryfamily.literaryfamily.exceptions.BestException;
import com.literaryfamily.literaryfamily.model.OnlineUser;
import com.literaryfamily.literaryfamily.model.ReturnResult;
import org.springframework.lang.Nullable;

/**
 * 所有涉及读写的方法都需要抛出 WriteError 数据库读写错误异常(这个是运行时异常,只需要抛出不需要处理)
 * 此接口所有方法均不检查用户身份,应该验证身份后在进行检查
 *
 * @author 滑稽/因果报应
 */
public interface LoginService {
	/**
	 * 用于注册的方法
	 *
	 * @param u                不带用户编号的完整用户信息
	 * @param verificationCode 短信验证码
	 * @return 成功时返回包含用户编号的用户信息
	 */
	User register(User u, String verificationCode);

	/**
	 * 用户登录的方法(包含普通的密码登录和短信登录)
	 * 登录时:
	 * 1.检查用户是否禁用,禁用时抛出用户异常
	 * 2.根据用户登录信息进行检索
	 * 2.1 如果是密码登录,检查密码是否匹配
	 * 2.1.1 密码验证:直接使用PasswordEncryptionUtil.verifyPassword进行验证,传入用户的密码
	 * 和从数据库里面取出的匹配项密码(记得完善一下防撞库攻击)
	 * 2.2 如果是短信登录,检查短信验证码是否匹配
	 * 3.检查是否有无离线时间的记录,如果有检查登录IP和地点(根据IP所在地获取)是否匹配,不匹配时抛出用户异常
	 * 3.2.匹配时检查有无对应Token记录如果有取出进行检查和解析,如果解析出错/过期视为没有,没有令牌时自动创建
	 * 新的令牌,并将数据库里面的过期时间设置为对应时间,令牌类型设置为保持登录(如果keepTime不为null,为空时使用
	 * 常量包里面的默认值)
	 * 其余情况设置为默认即可
	 * @param loginUser        登录的用户信息(密码部分为明码,允许使用手机号行登录)
	 * @param verificationCode 短信验证码(可选,用户密码为空时必填)
	 * @param keepTime         令牌过期时间,单位为毫秒
	 * @param ip               登录IP
	 * @return 登录结果, 仅包含用户id等必要信息,不包含密码的JWT令牌
	 */
	OnlineUser login(User loginUser, String verificationCode, @Nullable Long keepTime, String ip) throws BestException; // 登录

	/**
	 * 检查登录状态
	 * 流程:
	 * 1.检查提供的用户令牌是否有效
	 * 2.检查传入的用户ID,令牌是否在令牌表里面能够匹配上默认用途/保持登录用途的令牌(未过期)
	 * 2.1 如果匹配上,返回true
	 * 2.2 如果匹配不上,从数据库里面读取用户ID对应的相同用途的令牌(未过期),如果没有返回false
	 * 如果有对最多两个令牌进行解析,其中之一看看是否能够匹配上,如果匹配上返回true
	 * @param user 打包好的信息
	 * @return 验证结果
	 */
	boolean testLogin(OnlineUser user);

	/**
	 * 获取登陆历史记录,默认为5
	 *
	 * @param User 需要验证在线的用户
	 * @param size 获取记录数量
	 * @return 验证结果
	 */
	ReturnResult<UserLogin> LogHistory(User User, int size);

	/**
	 * 使用户退出登录
	 * 将用户ID对应的默认和保持登录用途(default_status, keep_login)的令牌过期
	 * @param userId 退出登录的用户ID
	 * @return 退出登录的结果
	 */
	ReturnResult<User> logout(Long userId); // 退出登录

	/**
	 * 让所有在线用户退出登录
	 *
	 * @return 被肘击下线的用户数量
	 */
	int logoutAllUser();

	/**
	 * 第三方登陆,此处使用授权链接的方式进行
	 *
	 * @param userId         登录的用户
	 * @param thirdId        第三方登陆ID
	 * @param type           平台类型:1:微信 2.qq
	 * @param devicePlatform 设备类型,1:pc网页,2:手机网页,3:微信小程序
	 * @return 登录结果, 如果用户为空说明未注册
	 */
	ReturnResult<User> thirdPartyLogin(Long userId, String thirdId, int type, int devicePlatform);

}